//
//  FileHandler.mm
//  Simple Tagger
//
//  Created by Thomas Bröckers on 21.02.08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "FileHandler.h"
#import "Track.h"
//#include <taglib/taglib.h>
//#include <taglib/fileref.h>
//#include <taglib/mpegfile.h>
//#include <taglib/id3v2tag.h>

NSString* const STAlbumTag = @"Album";
NSString* const STArtistAlbumTag = @"ArtistAlbum";
NSString* const STArtistTrackTag = @"ArtistTrack";
NSString* const STTitleTag = @"Title";
NSString* const STTrackNoTag = @"TrackNo";
NSString* const STTrackCountTag = @"TrackCount";
NSString* const STYearTag = @"Year";
NSString* const STDiscsTag = @"Discs";
NSString* const STGenreTag = @"Genre";
NSString* const STCoverTag = @"Cover";
NSString* const STLabelTag = @"Label";

@interface FileHandler (Private)
- (NSString *) replacePlaceholders:(NSString *) renamingSchema
                      WithMetadata:(NSDictionary *) Tags;
- (NSString *) resolvePlaceholders:(NSString *) renamingSchema
                      WithMetadata:(NSDictionary *) Tags;
- (void) writeMetaData:(NSDictionary *) tags 
             ToMp3File:(NSString *) filename;

@end

@implementation FileHandler

- (void) tagFiles:(NSArray *) fileList
       WithTracks:(NSArray *) trackList
        AndResult:(ResultEntry *) resultEntry {

    int i, tracks;
    NSMutableArray *coverList;
    
    NSMutableDictionary *tags = [[NSMutableDictionary alloc] init];
    
    [tags setObject:[resultEntry albumName] forKey:STAlbumTag];
    [tags setObject:[resultEntry artistName] forKey:STArtistAlbumTag];
    [tags setObject:[NSMutableArray arrayWithObject:[resultEntry genre]] forKey:STGenreTag];
    [tags setObject:[resultEntry yearOfPublication] forKey:STYearTag];
    [tags setObject:[resultEntry label] forKey:STLabelTag];
    [tags setObject:[NSString stringWithFormat:@"%d", [resultEntry numberOfDiscs]] forKey:STDiscsTag];
    
    // Anzahl der Tracks entweder der Songliste oder der Fileliste entnehmen
    if (trackList != nil) {
        tracks = [trackList count];
    }
    else {
        tracks = [fileList count];
    }
    [tags setObject:[NSString stringWithFormat:@"%d", tracks] forKey:STTrackCountTag];
    
    if ([resultEntry cover] != nil) {

        // Dictionary mit dem Cover erstellen und anschließend in ein Array schreiben
        NSImage *cover = [resultEntry cover];
        NSArray *representations = [cover representations];
        NSData *bitmapDataJPG = [NSBitmapImageRep representationOfImageRepsInArray:representations 
                                                                         usingType:NSJPEGFileType properties:nil];
        //[bitmapDataJPG writeToFile:@"/Users/Tom/Desktop/MyImage.jpeg" atomically:YES];
        NSBitmapImageRep *imageRepJPG = [NSBitmapImageRep imageRepWithData:bitmapDataJPG];
        // Die "Description" muss leer sein, da anderenfalls das Image in der MP3 Datei nicht korrekt angezeigt wird
        NSMutableDictionary *frontCover = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                      imageRepJPG, @"Image",
                                      @"Cover (front)", @"Picture Type", 
                                      @"image/jpeg", @"Mime Type", 
                                      @"", @"Description", nil];
        coverList = [NSMutableArray arrayWithObject:frontCover];
        [tags setObject:coverList forKey:STCoverTag];
    } 
    
    for (i = 0; i < [fileList count]; i++) {
                
        // Read the artist from the track itself and store it into the artist tag
        if ([trackList count] > 0) {
            NSString *artistTrack = [[trackList objectAtIndex:i] artist];
            if (artistTrack != nil) {
                [tags setObject:artistTrack forKey:STArtistTrackTag];
            }
        }
        
        [tags setObject:[NSString stringWithFormat:@"%d", (i + 1)] forKey:STTrackNoTag];
        
        // Wenn ein Track vorhanden ist, soll dieser verwendet werden, anderenfalls
        // wird der Dateiname als Basis genommen.
        if ([trackList count] > i) {
            [tags setObject:[[trackList objectAtIndex:i] title] forKey:STTitleTag];
        }
        else {
            [tags setObject:[[fileList objectAtIndex:i] lastPathComponent] forKey:STTitleTag];
        }
        
        [self writeMetaData:tags ToMp3File:[fileList objectAtIndex:i]];
        
        // Set new status
        NSString *newStatus = [NSString stringWithFormat:@"Tagging new files. %i files processed.", (i + 1)];
        [self sendStatusChange:newStatus];
        
    }
    
    [tags release];

}

- (void) renameFiles:(NSMutableArray *) fileList
          WithSchema:(NSString *) renamingSchema {

    int i;
    NSString *fileName, *newFileName, *newFileNameWithPath, *path, *extension, *newFileNameWithPathAndExt;
    
    if ([fileList count] > 0) {
    
        NSFileManager *fileManager = [NSFileManager defaultManager];
        for (i = 0; i < [fileList count]; i++) {
            fileName = [fileList objectAtIndex:i];
            extension = [[fileList objectAtIndex:i] pathExtension];
            path = [fileName stringByDeletingLastPathComponent];
            
            NSMutableDictionary *tags = [self readMetaDataFromMp3File:fileName];
            newFileName = [self replacePlaceholders:renamingSchema WithMetadata:tags];
            
            // Separate the folder from the filename mask and create the directory/directories
            NSString *folder = [newFileName stringByDeletingLastPathComponent];
            if (![folder isEqualToString:@""]) {
                NSString *newFolder = [path stringByAppendingPathComponent:folder];
                [fileManager createDirectoryAtPath:newFolder 
                       withIntermediateDirectories:YES 
                                        attributes:nil 
                                             error:nil];
            }
            
            newFileNameWithPath = [path stringByAppendingPathComponent:newFileName];
            newFileNameWithPathAndExt = [newFileNameWithPath stringByAppendingPathExtension:extension];
            
            // File umbenennen
            [fileManager moveItemAtPath:fileName toPath:newFileNameWithPathAndExt error:nil];
            
            // Eintrag in TableView anpassen
            [fileList replaceObjectAtIndex:i withObject:newFileNameWithPath];
            
            // Set new status
            NSString *newStatus = [NSString stringWithFormat:@"New file renamed. %i files processed.", (i + 1)];
            [self sendStatusChange:newStatus];
            
        }
    }
}

- (NSString *) buildPreviewForFiles:(NSMutableArray *) fileList
                         WithSchema:(NSString *) renamingSchema {
    
    NSString *returnValue = @"--";
    
    if ([fileList count] > 0 && [renamingSchema length] > 0) {
        
        NSMutableDictionary *tags = [self readMetaDataFromMp3File:[fileList objectAtIndex:0]];
        returnValue = [self resolvePlaceholders:renamingSchema WithMetadata:tags]; 
    
    }
        
    return returnValue;
}

- (NSString *) replacePlaceholders:(NSString *) renamingSchema
                      WithMetadata:(NSDictionary *) Tags {
    
    NSMutableString *aString = [renamingSchema mutableCopy];
    
    if ([renamingSchema length] > 0 && [Tags count] > 0) {
        
        // Replace all "/" with ":" because the ":" is interpreted as a "/"  
        NSMutableString *albumArtist = [[Tags objectForKey:STArtistAlbumTag] mutableCopy];
        [albumArtist replaceOccurrencesOfString: @"/" 
                                     withString: @":" 
                                        options: 0 
                                          range: NSMakeRange(0, [albumArtist length])];
        [aString replaceOccurrencesOfString: @"%a" 
                                 withString: albumArtist 
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        NSMutableString *album = [[Tags objectForKey:STAlbumTag] mutableCopy];
        [album replaceOccurrencesOfString: @"/" 
                               withString: @":" 
                                  options: 0 
                                    range: NSMakeRange(0, [album length])];        
        [aString replaceOccurrencesOfString: @"%b" 
                                 withString: album  
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        NSMutableString *title = [[Tags objectForKey:STTitleTag] mutableCopy];
        [title replaceOccurrencesOfString: @"/" 
                               withString: @":" 
                                  options: 0 
                                    range: NSMakeRange(0, [title length])];                
        [aString replaceOccurrencesOfString: @"%t" 
                                 withString: title
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        [aString replaceOccurrencesOfString: @"%n" 
                                 withString: [NSString stringWithFormat:@"%.2d", [[Tags objectForKey:STTrackNoTag] intValue]]
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        [aString replaceOccurrencesOfString: @"%y" 
                                 withString: [Tags objectForKey:STYearTag]
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        NSMutableString *trackArtist = [[Tags objectForKey:STArtistTrackTag] mutableCopy];
        [trackArtist replaceOccurrencesOfString: @"/" 
                                     withString: @":" 
                                        options: 0 
                                          range: NSMakeRange(0, [trackArtist length])];                        
        [aString replaceOccurrencesOfString: @"%r" 
                                 withString: trackArtist
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];    
    }

    return aString;
}

- (NSString *) resolvePlaceholders:(NSString *) renamingSchema
                      WithMetadata:(NSDictionary *) Tags {
    
    NSMutableString *aString = [renamingSchema mutableCopy];
    
    if ([renamingSchema length] > 0 && [Tags count] > 0) {
        
        [aString replaceOccurrencesOfString: @"%a" 
                                 withString: [Tags objectForKey:STArtistAlbumTag] 
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        [aString replaceOccurrencesOfString: @"%b" 
                                 withString: [Tags objectForKey:STAlbumTag]  
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        [aString replaceOccurrencesOfString: @"%t" 
                                 withString: [Tags objectForKey:STTitleTag]
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        [aString replaceOccurrencesOfString: @"%n" 
                                 withString: [NSString stringWithFormat:@"%.2d", [[Tags objectForKey:STTrackNoTag] intValue]]
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        [aString replaceOccurrencesOfString: @"%y" 
                                 withString: [Tags objectForKey:STYearTag]
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];
        
        [aString replaceOccurrencesOfString: @"%r" 
                                 withString: [Tags objectForKey:STArtistTrackTag]
                                    options: 0 
                                      range: NSMakeRange(0, [aString length])];    
    }
    
    return aString;
}

- (void) deleteTagsInMp3Files:(NSArray *) fileList {

    int i;
    
    [self sendStatusChange:@"Start removing existing tags."];
    
    // Tag Objekt erstellen
    NSMutableDictionary *genreList = NULL;
    TagAPI *Tag = [[TagAPI alloc] initWithGenreList:genreList];

    
    // Files einzeln durchlaufen und taggen
    for (i = 0; i < [fileList count]; i++) {
        
        [Tag examineFile:[fileList objectAtIndex:i]];
        [Tag dropTag:NO];    
        
        // Set new status
        NSString *newStatus = [NSString stringWithFormat:@"Removing existing tags. %i files processed.", (i + 1)];
        [self sendStatusChange:newStatus];
        
    }
    
    [self sendStatusChange:@"Finished removing existing tags."];
    
    [Tag release];
}

- (void) sendStatusChange:(NSString *) newStatus {
    
    NSDictionary *dict = [NSDictionary dictionaryWithObject:newStatus forKey:@"status"];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"STStatusChanged" 
                                                        object:self 
                                                      userInfo:dict];
    
}

- (NSMutableDictionary *) readMetaDataFromMp3File:(NSString *) filename {
    
    NSMutableDictionary *metadata = [[[NSMutableDictionary alloc] init] autorelease];
    NSMutableDictionary *genreList = NULL;
    TagAPI *Tag = [[TagAPI alloc] initWithGenreList:genreList];
    [Tag examineFile:filename];
    
    if ([Tag v1TagPresent] || [Tag v2TagPresent]) {
        
        // Artist
        if ([Tag getArtist] != nil) {
            [metadata setObject:[Tag getArtist] forKey:STArtistTrackTag];
        }
        else {
            [metadata setObject:@"" forKey:STArtistTrackTag];
        }
        
        // Album Artist
        NSString *artistAlbum;
        if ([[Tag getFrame:@"TPE2"] count] > 0) {
            id3V2Frame *frame = [[Tag getFrame:@"TPE2"] objectAtIndex:0];
            artistAlbum = [frame getTextFromFrame];
        }
        else {
            // No album artist existent, so use the track artist
            artistAlbum = [Tag getArtist] != nil ? [Tag getArtist] : @"";
        }
        [metadata setObject:artistAlbum forKey:STArtistAlbumTag];
        
        // Album
        if ([Tag getAlbum] != nil) {
            [metadata setObject:[Tag getAlbum] forKey:STAlbumTag];
        }
        else {
            [metadata setObject:@"" forKey:STAlbumTag];
        }
        
        // Title
        if ([Tag getTitle] != nil) {
            [metadata setObject:[Tag getTitle] forKey:STTitleTag];
        }
        else {
            [metadata setObject:@"" forKey:STTitleTag];            
        }
        
        // Year
        if ([Tag getYear] > 0) {
            [metadata setObject:[NSString stringWithFormat:@"%d", [Tag getYear]] forKey:STYearTag];
        }
        else {
            [metadata setObject:@"" forKey:STYearTag];
        }
        
        // Track Number
        if ([Tag getTrack] > 0) {
            NSString *trackNo = [NSString stringWithFormat:@"%d", [Tag getTrack]];
            [metadata setObject:trackNo forKey:STTrackNoTag];
        }
        else {
            [metadata setObject:@"" forKey:STTrackNoTag];
        }
        
        // Track Number Total
        if ([Tag getTotalNumberTracks] > 0) {
            NSString *tracks = [NSString stringWithFormat:@"%d", [Tag getTotalNumberTracks]];
            [metadata setObject:tracks forKey:STTrackCountTag];
        }
        else {
            [metadata setObject:@"" forKey:STTrackCountTag];
        }
        
        // Number of Discs
        if ([Tag getTotalNumberDisks] > 0) {
            NSString *discs = [NSString stringWithFormat:@"%d", ([Tag getTotalNumberDisks] > 0 ? [Tag getTotalNumberDisks] : 1)];
            [metadata setObject:discs forKey:STDiscsTag];
        }
        else {
            [metadata setObject:@"" forKey:STDiscsTag];
        }
        
        // Genre
        if ([[Tag getGenreNames] count] > 0) {
            [metadata setObject:[[Tag getGenreNames] objectAtIndex:0] forKey:STGenreTag];
        }
        else {
            [metadata setObject:@"" forKey:STGenreTag];
        }
        
        // TODO: getLabel doesn't work. Why?
        NSString *label = @"";
        [metadata setObject:label forKey:STLabelTag];
        
        // Cover
        if ([[Tag getImage] count] > 0) {
            NSBitmapImageRep *bitmap = [[[Tag getImage] objectAtIndex:0] objectForKey:@"Image"];
            NSData *data = [bitmap representationUsingType:NSJPEGFileType properties:nil];
            NSImage *image = [[NSImage alloc] initWithData:data];
            [metadata setObject:image forKey:STCoverTag];
            [image release];
        }
    }
    
    [Tag release];
         
    return metadata;
     
}

- (void) writeMetaData:(NSDictionary *) tags 
             ToMp3File:(NSString *) filename {

    /*
     // Try TagLib Library
     TagLib::MPEG::File file([[fileList objectAtIndex:i] fileSystemRepresentation]);
     TagLib::String s;
     s = file.tag()->artist();
     NSString *log = [NSString stringWithUTF8String:s.toCString(true)];
     NSLog(@"artist:%@", log);
     */
    
    NSMutableDictionary *genreList = NULL;
    TagAPI *Tag = [[TagAPI alloc] initWithGenreList:genreList];

    [Tag examineFile:filename];
        
    // Artist
    if ([tags objectForKey:STArtistTrackTag] != nil) {
        [Tag setArtist:[tags objectForKey:STArtistTrackTag]];
    }
    else {
        [Tag setArtist:[tags objectForKey:STArtistAlbumTag]];
    }
    
    [Tag setTitle:[tags objectForKey:STTitleTag]];
    [Tag setAlbum:[tags objectForKey:STAlbumTag]];
    [Tag setGenreName:[tags objectForKey:STGenreTag]];
    //[Tag setLabel:(NSString *)[tags objectForKey:STLabelTag]];
    [Tag setYear:[[tags objectForKey:STYearTag] intValue]];
    [Tag setTrack:[[tags objectForKey:STTrackNoTag] intValue] totalTracks:[[tags objectForKey:STTrackCountTag] intValue]];
    int discs = [[tags objectForKey:STDiscsTag] intValue];
    [Tag setDisk:discs totalDisks:discs];
    if ([tags objectForKey:STCoverTag] != nil) [Tag setImages:[tags objectForKey:STCoverTag]];
    
    // Vorhandene Kommentare immer löschen
    [Tag setComments:@""];                
    
    // Album Artist
    id3V2Frame *frame = [[id3V2Frame alloc] initTextFrame:@"TPE2" 
                                                firstflag:0 
                                               secondFlag:0 
                                                     text:[tags objectForKey:STArtistAlbumTag]
                                             withEncoding:3 
                                                  version:4];
    [Tag setFrame:frame replace:YES];    
    [Tag updateFile];
    [frame release];
    [Tag release];

}

@end
